Skip to content

feat(windows): prefer PowerShell defaults for shell tools#16069

Open
Hona wants to merge 10 commits intoanomalyco:devfrom
Hona:experimental/windows-shells
Open

feat(windows): prefer PowerShell defaults for shell tools#16069
Hona wants to merge 10 commits intoanomalyco:devfrom
Hona:experimental/windows-shells

Conversation

@Hona
Copy link
Member

@Hona Hona commented Mar 5, 2026

Summary

  • prefer pwsh, then powershell, before Git Bash and cmd.exe on Windows when SHELL is unset for both Shell.preferred() and Shell.acceptable()
  • keep the existing Git Bash path resolution fallback intact instead of switching to plain bash lookup
  • show the active process.platform and resolved shell name in the bash tool definition so agents can see the runtime context

Use pwsh and powershell before Git Bash when SHELL is unset on Windows so the default shell matches native expectations more closely. Surface the active OS and shell in the bash tool definition so agents can reason about the runtime they are executing in.
Copilot AI review requested due to automatic review settings March 5, 2026 00:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts Windows shell selection defaults to prefer PowerShell-family shells and improves runtime transparency for the bash tool by surfacing OS/shell context in its description.

Changes:

  • Update Shell.preferred() / Shell.acceptable() to prefer pwsh, then powershell, when SHELL is unset on Windows.
  • Keep existing Windows Git Bash path-resolution fallback behavior intact.
  • Inject process.platform and a resolved shell name into the bash tool description template.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
packages/opencode/src/tool/bash.txt Adds OS + resolved shell placeholders to the tool description.
packages/opencode/src/tool/bash.ts Resolves shell “display name” and substitutes OS/shell into the tool description.
packages/opencode/src/shell/shell.ts Introduces base() helper and changes Windows default shell preference order.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +65 to 69
if (process.platform === "win32") {
if (Bun.which("pwsh")) return "pwsh"
if (Bun.which("powershell")) return "powershell"
}
return fallback()
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Windows, returning the bare string "pwsh"/"powershell" here can break downstream logic that assumes shells ending with "sh" are POSIX shells (e.g. Pty.create() appends -l when command.endsWith("sh"), which matches pwsh). Consider returning the resolved executable path from Bun.which(...) (typically ends with .exe) or otherwise ensuring the returned value won’t match that heuristic; also consider de-duplicating the pwsh/powershell selection logic between preferred and acceptable to avoid drift.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +41
if (process.platform === "win32") return path.win32.basename(file, ".exe")
return path.basename(file)
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

base() is used for the blacklist check, but on Windows path.win32.basename(file, ".exe") only strips the extension if the casing matches exactly, and it preserves casing of the name. If SHELL is something like Nu.EXE or NU.EXE, base() won’t produce "nu", so the blacklist won’t apply. Consider using path.win32.parse(file).name.toLowerCase() (or lowercasing the basename result) to make the check robust.

Suggested change
if (process.platform === "win32") return path.win32.basename(file, ".exe")
return path.basename(file)
if (process.platform === "win32") {
// Use parse().name to strip the extension in a case-insensitive way, then normalize to lowercase
return path.win32.parse(file).name.toLowerCase()
}
// On non-Windows, just take the basename and normalize to lowercase
return path.basename(file).toLowerCase()

Copilot uses AI. Check for mistakes.
Comment on lines 62 to +78
export const preferred = lazy(() => {
const s = process.env.SHELL
if (s) return s
if (process.platform === "win32") {
if (Bun.which("pwsh")) return "pwsh"
if (Bun.which("powershell")) return "powershell"
}
return fallback()
})

export const acceptable = lazy(() => {
const s = process.env.SHELL
if (s && !BLACKLIST.has(process.platform === "win32" ? path.win32.basename(s) : path.basename(s))) return s
if (s && !BLACKLIST.has(base(s))) return s
if (process.platform === "win32") {
if (Bun.which("pwsh")) return "pwsh"
if (Bun.which("powershell")) return "powershell"
}
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR changes Windows shell selection behavior (pwsh/powershell preference and updated blacklist logic), but there are no unit tests covering Shell.preferred() / Shell.acceptable() on win32 or the interaction with call sites like Pty.create(). Adding targeted tests (with platform/env/which stubbing) would help prevent regressions like incorrect args being appended or blacklisted shells slipping through.

Copilot uses AI. Check for mistakes.
Remove the one-off helper used for shell name normalization and keep the Windows blacklist check inline. This keeps the shell selection logic simpler without changing behavior.
@Hona Hona changed the title fix(windows): prefer PowerShell defaults for shell tools feat(windows): prefer PowerShell defaults for shell tools Mar 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants